///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_CVIEW_H_
#define _BUOLA_IMAGE_CVIEW_H_

#include <buola/image.h>
#include <buola/image/cpixel.h>
#include <buola/image/traits.h>
#include <buola/iterator/range.h>

namespace buola { namespace img {

DECLARE_NAME(RawBytes)
    
template<typename tView>
class CViewRow
{
    typedef typename tView::TXIterator TIterator;
public:
    CViewRow(const tView &pView,int py)
        :   mView(pView)
        ,   y(py)
    {}

    TIterator begin() const   {   return mView.XIterator(0,y);                }
    TIterator end() const     {   return mView.XIterator(mView.Width(),y);    }
    typename tView::TPointer RawData() const    {   return mView.RawData(0,y);}
    
private:
    const tView &mView;
    int y;
};
    
template<typename tView>
class CViewCol
{
    typedef typename tView::TYIterator TIterator;
public:
    CViewCol(const tView &pView,int px)
        :   mView(pView)
        ,   x(px)
    {}

    TIterator begin()   {   return mView.YIterator(x,0);                }
    TIterator end()     {   return mView.YIterator(x,mView.Height());   }

private:
    const tView &mView;
    int x;
};

template<typename tFormat,int vPixelAdvance=1>
class CView
{
public:
    typedef tFormat TFormat;
    typedef CView<tFormat,vPixelAdvance> TView;

    static const int cPixelAdvance=vPixelAdvance;
    static const int cNumDimensions=2;
    static const std::size_t cChannels=tFormat::TColorSpace::cChannels;
    typedef CPixel<tFormat> TPixel;

    typedef typename tFormat::TChannel::TType TValue;
    typedef const TValue* TPointer;
    typedef CPixelXIterator<const TValue,tFormat,vPixelAdvance> TXIterator;
    typedef CPixelYIterator<const TValue,tFormat> TYIterator;
    typedef typename TXIterator::TAccessor TAccessor;
    typedef CViewRow<TView> TRow;
    typedef CViewCol<TView> TCol;
    
    template<typename tF=tFormat,int pA=vPixelAdvance>
    struct MRebind
    {
        typedef CView<tF,pA> type;
    };
    
    CView()
        :   mData(nullptr)
        ,   mSize(0,0)
    {}

    CView(TPointer pData,const CSize_i &pSize)
        :   mData((uint8_t*)const_cast<TValue*>(pData))
        ,   mSize(pSize)
        ,   mStride(tFormat::mPixelSize*pSize.x)
    {}
        
    CView(TPointer pData,const CSize_i &pSize,ptrdiff_t pStride)
        :   mData((uint8_t*)const_cast<TValue*>(pData))
        ,   mSize(pSize)
        ,   mStride(pStride)
    {}

    CView(NRawBytes,uint8_t *pData,const CSize_i &pSize,ptrdiff_t pStride)
        :   mData(pData)
        ,   mSize(pSize)
        ,   mStride(pStride)
    {}

    CView(const CView &pRH)=default;
    CView &operator=(const CView &pRH)=default;

    explicit CView(const CAnyImage&);

public:
    const CSize_i &Size() const                {   return mSize;   }
    int Width() const                           {   return mSize.x; }
    int Height() const                          {   return mSize.y; }
    int Points() const                          {   return Width()*Height();    }
    ptrdiff_t Stride() const                    {   return mStride; }
    bool Continuous() const                   {   return (mStride==vPixelAdvance*tFormat::mPixelSize*mSize.x);   }
    
    TPointer RawData(int x,int y) const    {    return TPointer(mData+mStride*y)+cChannels*vPixelAdvance*x;    }
    TPointer RawData() const                            {   return TPointer(mData);             }
    
    TAccessor operator()(const CPoint_i &pP) const         {   return TAccessor(RawData(pP.x,pP.y));           }
    TAccessor operator()(int x,int y) const                 {   return TAccessor(RawData(x,y));                 }

    const uint8_t *RawBytes() const                     {   return mData;                       }

    TXIterator XIterator(int x,int y) const      {   return TXIterator(RawData(x,y));    }
    TYIterator YIterator(int x,int y) const      {   return TYIterator(RawData(x,y),mStride);    }
    
    TCol Col(int x) const        {   return {*this,x};            }
    TRow Row(int y) const        {   return {*this,y};            }

    template<typename tIterator>
    CPoint_i GetPosition(const tIterator &pI) const
    {
        ptrdiff_t lOffset=(uint8_t*)pI.GetPointer()-mData;
        return CPoint_i((lOffset%mStride)/(vPixelAdvance*tFormat::mPixelSize),lOffset/mStride);
    }

    bool operator==(const CView &pRH) const
    {
        return (mData==pRH.mData&&mSize==pRH.mSize&&mStride==pRH.mStride);
    }

    bool operator!=(const CView &pRH) const
    {
        return !operator==(pRH);
    }
    
    explicit operator bool() const    {   return mSize.x!=0;    }

protected:
    uint8_t *mData;
    CSize_i mSize;
    ptrdiff_t mStride;
};

template<typename tFormat>
CRange<typename CView<tFormat>::TPointer> raw_range(const CView<tFormat> &pView)
{
    return {pView.RawData(),pView.RawData()+pView.Points()};
}

template<typename tFormat,int vPixelAdvance>
struct MIsView<CView<tFormat,vPixelAdvance>>
{
    static const bool value=true;
};

/*namespace image*/ } /*namespace buola*/ }

#endif
